package give.replay;

import give.comm.Message;
import give.comm.StatusMessage;
import give.comm.handling.MessageHandlingThread;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: koller
 * Date: Oct 19, 2009
 * Time: 10:05:12 AM
 * To change this template use File | Settings | File Templates.
 */
public class GameEventReplayer {
    private static final boolean DEBUG=false;
    public static enum State {
        UNINITIALIZED, PLAYING, PAUSED
    };
    private List<GameEvent> events;
    protected int currentEvent;
    private Thread playbackThread;
    private double speedfactor;
    private MessageHandlingThread handler;
    private State state;
    private boolean eventsSorted;

    public GameEventReplayer() {
        events = new ArrayList<GameEvent>();
        speedfactor = 1;
        state = State.UNINITIALIZED;
        eventsSorted = false;
    }

    public void addEvent(Date timestamp, Message message) {
        if (eventsSorted) {
            throw new UnsupportedOperationException("Can't add events after playback has started");
        }

        events.add(new GameEvent(timestamp, message));
    }

    public void addAll(Collection<GameEvent> newEvents) {
        if (eventsSorted) {
            throw new UnsupportedOperationException("Can't add events after playback has started");
        }

        events.addAll(newEvents);
    }

    public void clear() {
        events.clear();
        eventsSorted = false;
    }

    public void reset() {
        pause();
        currentEvent = 0;
    }

    public List<GameEvent> getEvents() {
        return events;
    }

    public int getCurrentEventIndex() {
        return currentEvent;
    }

    public synchronized void replay(MessageHandlingThread handler, boolean startPlayback) {
        sortEvents();
        currentEvent = 0;
        this.handler = handler;

        if (startPlayback) {
            playback();
        } else {
            state = State.PAUSED;
        }
    }

    public synchronized void pause() {
        if (playbackThread != null) {
            playbackThread.interrupt();
            playbackThread = null;
            state = State.PAUSED;
        }
    }

    public synchronized void resume() {
        if (playbackThread == null) {
            playback();
        }
    }

    public synchronized void jumpTo(int eventIndex) {
        if (state == State.PAUSED) {
            fastforward(eventIndex);
        }
    }

    public void setSpeedfactor(double speedfactor) {
        this.speedfactor = speedfactor;
    }

    public State getState() {
        return state;
    }

    private void fastforward(int eventIndex) {
        while (currentEvent < eventIndex) {
            step();
        }
    }

    private void sortEvents() {
        if (!eventsSorted) {
            Collections.sort(events);
            eventsSorted = true;
        }
    }

    private void playback() {
        playbackThread = new PlaybackThread();
        playbackThread.start();
        state = State.PLAYING;
    }

    private void step() {
        GameEvent event = events.get(currentEvent);

        if(DEBUG) if( ! (event.message instanceof StatusMessage) ) System.err.println("replay: " + currentEvent + " " + event.message);

        handler.offer(event.message);
        currentEvent++;
    }

    private class PlaybackThread extends Thread {
        @Override
        public void run() {
            try {
                while (currentEvent < events.size()) {
                    GameEvent event = events.get(currentEvent);

                    if (currentEvent > 0) {
                        Thread.sleep((long) ((event.timestamp.getTime() - events.get(currentEvent - 1).timestamp.getTime()) / speedfactor));
                    }

                    step();
                }
            } catch (InterruptedException e) {
                if(DEBUG) System.err.println("replay thread interrupted");
            } finally {
                playbackThread = null;
                if(DEBUG) System.err.println("replay thread ended");
            }
        }
    }
}
